#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <pthread.h>
#include <vector>
#include <set>
#include <unistd.h>
#include <math.h>
#include "getFilenameLinux.h"
#include "mapMerge.h"

using namespace std;


struct thread_data{
    // 线程id
    int  thread_id;
    // 所要处理的文件列表
    vector<string> fileNames;
    // 存放词频
    map<string,int> wmap;
};

// 词频统计函数
void *wordCountFun(void *threadarg)
{
    // 得到thread_data的结构体数据
    struct thread_data *my_data;
    my_data = (struct thread_data *) threadarg;

    map<string,int> wmap;
    int i=0;
    for (const auto &filename : my_data->fileNames) {
        i++;
        cout << "Thread ID : " << my_data->thread_id <<" is processing" << " the file " << i << " : " << filename << endl;
        ifstream fin(filename.c_str());                    //  read  in  str[]
        string  temp;
        while(fin>>temp)
            wmap[temp]++;
        my_data->wmap = wmap;
        // display_map(wmap);

        fin.close();
    }
    cout<<endl<<endl;
    pthread_exit(NULL);
}

   
int main(int argc, char* argv[])
{
    // 判断用户输入是否正确
    if (argc !=3 ){
        cout<<"请输入要统计的文件夹和线程数，如下所示，第一个参数是文件夹路径，第二个参数是线程数："<<endl;
        cout<<"./word_count ./test 2"<<endl;
        cout<<"线程大于文件数时，会自动修改为文件数目"<<endl;
        return 0;
    }

    cout<< "word count files dir：\n" <<"    "<<argv[1] << endl;
    cout<<endl<<endl;
    
    pthread_attr_t attr;
    void *status;
     // 初始化并设置线程为可连接的（joinable）
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    vector<string> fileNames;
    // 得到所有的文件名
    recursion_scan_dir_file(argv[1], 1, fileNames, "");
    // scan_one_dir("test", fileNames);
    
    int rc;
    int i=0;
    int every_thread_file_num = 1;
    // 线程数目
    int NUM_THREADS = atoi(argv[2]);
    // 限制线程个数
    if (fileNames.size() > NUM_THREADS){
        every_thread_file_num = fileNames.size() / NUM_THREADS;
    }else{
        NUM_THREADS = fileNames.size();
    }

    pthread_t threads[NUM_THREADS];
    struct thread_data td[NUM_THREADS];

    for( i=0; i < NUM_THREADS; i++ ){
        cout <<"main() : creating thread, " << i ;
        td[i].thread_id = i;
        // 找到第一个迭代器
        vector<string>::const_iterator First = fileNames.begin() + i * every_thread_file_num; 
        vector<string>::const_iterator Second;
        // 前面的按着计算结果划分，最后一个线程计算统计所有剩余的文件
        if (i == NUM_THREADS - 1){
            Second = fileNames.end(); 
        }else{
            // 找到第二个迭代器
            Second = fileNames.begin() + (i+1) * every_thread_file_num; 
        }
        //使用assign() 成员函数将所有的文件名对应位置的值存入到每个线程的数组中
        td[i].fileNames.assign(First, Second); 
        
   
        cout<<" has the following files : \n";
        for (const auto &ph : td[i].fileNames) {
            cout << "    "<<(string)ph << "\n";
        }

        rc = pthread_create(&threads[i], NULL,
                            wordCountFun, (void *)&td[i]);
        if (rc){
            cout << "Error:unable to create thread," << rc << endl;
            exit(-1);
        }
        cout<<endl<<endl;
    }
    pthread_attr_destroy(&attr);

    // 删除属性，并等待其他线程结束
    multimap<string,int> dest1;
    for( i=0; i < NUM_THREADS; i++ ){
        rc = pthread_join(threads[i], &status);
        if (rc){
            cout << "Error:unable to join," << rc << endl;
            exit(-1);
        }
        
        cout << "Main: completed thread id :" << i;
        // 合并所有线程中的map数据
        dest1.insert(td[i].wmap.begin(), td[i].wmap.end());
        cout << "  exiting with status :" << status << endl;
    }
    cout<<"process over"<<endl<<endl;

    // 得到文件中所有的单词
    set<std::string> keys;
    for (const auto &i : dest1) {
        keys.insert(i.first);
    }
    // 输出每个单词的合并结果
    cout <<"Merge and show result :"<<endl<<endl;
    for (const auto &i : keys) {
        cout << "Sum of "<< i <<": " << accumulate_range(dest1, i, 0) << std::endl;
    }
    pthread_exit(NULL);

    return 0;
}
   